package com.glela.common.util;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class CryptUtils
{

	private final static String DES = "DES";
	private final static String MD5 = "MD5";
	private final static String SHA = "SHA1";
	public static String AES_ECB = "AES/ECB/PKCS5Padding";
	public static String AES_CBC = "AES/CBC/PKCS5Padding";
	private static long orderNum = 0l;
	private static String date;

	final static int AES_KEY_LENGTH = 32;

	// 864273525a44818245d9c4910c8c9ab7
	final static byte[] saltConst = { (byte) 0x86, (byte) 0x42, (byte) 0x73, (byte) 0x52, (byte) 0x5A, (byte) 0x44, (byte) 0x81,
			(byte) 0x82, (byte) 0x45, (byte) 0xD9, (byte) 0xC4, (byte) 0x91, (byte) 0x0C, (byte) 0x8C, (byte) 0x9A, (byte) 0xB7 };

	// 82ef6e6ecfe49e4084355995e42293ec
	final static byte[] ivConst = { (byte) 0x82, (byte) 0xEF, (byte) 0x6E, (byte) 0x6E, (byte) 0xCF, (byte) 0xE4, (byte) 0x9E,
			(byte) 0x40, (byte) 0x84, (byte) 0x35, (byte) 0x59, (byte) 0x95, (byte) 0xE4, (byte) 0x22, (byte) 0x93, (byte) 0xEC };

	final static IvParameterSpec ivConstParams = new IvParameterSpec(ivConst);

	/**
	 * 解密
	 * 
	 * @param key
	 *            密钥
	 * @param source
	 *            待解密内容
	 * @return
	 */
	public static byte[] aesCbcDecrypt(byte[] key, byte[] source)
	{
		try {
			Cipher cipher;
			SecretKeySpec skeySpec = new SecretKeySpec(key, 0, key.length, "AES");
			cipher = Cipher.getInstance(AES_CBC);
			cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivConstParams);
			//			cipher.update(source, 0, source.length);
			byte[] out = cipher.doFinal(source, 0, source.length);
			return out;
		}
		catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			e.printStackTrace();
		}
		catch (InvalidKeyException e) {
			e.printStackTrace();
		}
		catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		}
		catch (BadPaddingException e) {
			e.printStackTrace();
		}
		catch (InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 加密
	 * 
	 * @param key
	 *            密钥
	 * @param source
	 *            加密内容str.getBytes()
	 * @return
	 */
	public static byte[] aesCbcEncrypt(byte[] key, byte[] source)
	{
		try {
			Cipher cipher;
			SecretKeySpec skeySpec = new SecretKeySpec(key, 0, key.length, "AES");
			cipher = Cipher.getInstance(AES_CBC);
			cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivConstParams);
			//			cipher.update(source, 0, source.length);
			byte[] out = cipher.doFinal(source, 0, source.length);
			return out;
		}
		catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			e.printStackTrace();
		}
		catch (InvalidKeyException e) {
			e.printStackTrace();
		}
		catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		}
		catch (BadPaddingException e) {
			e.printStackTrace();
		}
		catch (InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		}
		return null;
	}

	public static String byte2hex(byte[] b)
	{
		StringBuilder hs = new StringBuilder();
		String stmp;
		for (int n = 0; b != null && n < b.length; n++) {
			stmp = Integer.toHexString(b[n] & 0XFF);
			if (stmp.length() == 1) {
				hs.append('0');
			}
			hs.append(stmp);
		}
		return hs.toString().toUpperCase();
	}

	public final static String decrypt(String data, String key)
	{
		return new String(CryptUtils.decrypt(CryptUtils.hex2byte(data.getBytes()), key.getBytes()));
	}

	/**
	 * 派生密钥
	 * 
	 * @param password
	 * @return
	 */
	public static byte[] deriveAesKey(String password)
	{
		int iterations = 3;
		char[] chars = password.toCharArray();
		byte[] salt = saltConst;

		byte[] hash = null;
		PBEKeySpec spec = new PBEKeySpec(chars, salt, iterations, AES_KEY_LENGTH * 8);
		SecretKeyFactory skf;
		try {
			skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
			hash = skf.generateSecret(spec).getEncoded();
		}
		catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		catch (InvalidKeySpecException e) {
			e.printStackTrace();
		}
		return hash;
	}

	public final static String encrypt(String data, String key)
	{
		if (data != null) {
			try {
				return CryptUtils.byte2hex(CryptUtils.encrypt(data.getBytes(), key.getBytes()));
			}
			catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		return null;
	}

	/**
	 * 生成订单编号
	 * 
	 * @return
	 */
	public static synchronized String getOrderNo()
	{
		String str = new SimpleDateFormat("yyMMddHHmm").format(new Date());
		if (date == null || !date.equals(str)) {
			date = str;
			orderNum = 0l;
		}
		orderNum++;
		long orderNo = Long.parseLong(date) * 10000;
		orderNo += orderNum;
		;
		return orderNo + "";
	}

	/**
	 * 获得唯一的UUID
	 * 
	 * @return
	 */
	public static String getUUID()
	{
		UUID uuid = UUID.randomUUID();
		String str = uuid.toString();
		// 去掉"-"符号  
		String temp = str.substring(0, 8) + str.substring(9, 13) + str.substring(14, 18) + str.substring(19, 23)
				+ str.substring(24);
		return temp;
	}

	/**
	 * 获得指定数量的UUID(批量获取)
	 * 
	 * @param number
	 * @return
	 */
	public static String[] getUUID(int number)
	{
		if (number < 1) {
			return null;
		}
		String[] ss = new String[number];
		for (int i = 0; i < number; i++) {
			ss[i] = CryptUtils.getUUID();
		}
		return ss;
	}

	public static byte[] hex2byte(byte[] b)
	{
		if (b.length % 2 != 0) {
			throw new IllegalArgumentException();
		}
		byte[] b2 = new byte[b.length / 2];
		for (int n = 0; n < b.length; n += 2) {
			String item = new String(b, n, 2);
			b2[n / 2] = (byte) Integer.parseInt(item, 16);
		}
		return b2;
	}

	public static void main(String args[])
	{
//		//生成派生公钥
//		byte[] key = CryptUtils.deriveAesKey("618");
//		System.out.println("EDA8-0833-9F2D-F1B61".length());
//		//加密
//		byte[] encryptedData = CryptUtils.aesCbcEncrypt(key, "EDA8-0833-9F2D-F1B61".getBytes());
//		System.out.println("加密后字符：");
//		System.out.println(new String(encryptedData) + "," + new String(encryptedData).length());
//		//解密
//		byte[] decryptedData = CryptUtils.aesCbcDecrypt(key, encryptedData);
//		System.out.println("解密后字符：");
//		System.out.println(new String(decryptedData));
		System.out.println(getUUID());
	}

	public static String md5(String source)
	{
		return CryptUtils.encryption(source, MD5);
	}

	public static String sha(String source)
	{
		return CryptUtils.encryption(source, SHA);
	}

	private static byte[] decrypt(byte[] src, byte[] key) throws RuntimeException
	{
		try {
			SecureRandom sr = new SecureRandom();
			DESKeySpec dks = new DESKeySpec(key);

			SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
			SecretKey securekey = keyFactory.generateSecret(dks);
			Cipher cipher = Cipher.getInstance(DES);
			cipher.init(Cipher.DECRYPT_MODE, securekey, sr);

			return cipher.doFinal(src);
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private static byte[] encrypt(byte[] src, byte[] key) throws RuntimeException
	{
		try {
			SecureRandom sr = new SecureRandom();
			DESKeySpec dks = new DESKeySpec(key);

			SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
			SecretKey securekey = keyFactory.generateSecret(dks);
			Cipher cipher = Cipher.getInstance(DES);
			cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);

			return cipher.doFinal(src);
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 加密内容
	 * 
	 * @param plainText
	 *            加密的内容
	 * @param type
	 *            加密的方式
	 * @return
	 */
	private static String encryption(String plainText, String type)
	{
		String re_md5 = new String();
		try {
			MessageDigest md = MessageDigest.getInstance(type);
			md.update(plainText.getBytes());
			byte b[] = md.digest();
			int i;
			StringBuffer buf = new StringBuffer("");
			for (byte element : b) {
				i = element;
				if (i < 0) {
					i += 256;
				}
				if (i < 16) {
					buf.append("0");
				}
				buf.append(Integer.toHexString(i));
			}
			re_md5 = buf.toString();
		}
		catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return re_md5;
	}
}